package com.ebupt.migu.music.autotri.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.ebupt.migu.music.autotri.entity.MessageInfo;
import com.ebupt.migu.music.autotri.mapper.EffectTotalMapper;
import com.ebupt.migu.music.autotri.service.EffectTotalService;
import com.ebupt.migu.music.commons.constants.Sysconst;
import com.ebupt.migu.music.commons.utils.DateUtil;
import com.ebupt.migu.music.commons.utils.FtpUtil;
import com.ebupt.migu.music.commons.utils.HttpUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTPClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.text.DecimalFormat;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class EffectTotalServiceImpl implements EffectTotalService {

    /**
     * push策略参数
     */
    @Value("${push.ip}")
    private String PUSH_IP;
    @Value("${push.app_id}")
    private String PUSH_APP_ID;
    @Value("${push.app_key}")
    private String PUSH_APP_KEY;
    @Value("${push.app_secret}")
    private String PUSH_APP_SECRET;
    @Value("${push.push_url}")
    private String PUSH_URL;
    @Value("${push.is_sync}")
    private boolean IS_SYNC;
    @Value("${push.push_result_url}")
    private String pushResultUrl;
    @Value("${push.pushChannel}")
    private String pushChannel;
    @Value("${push.push_result_duration}")
    private String pushResultDuration;
    @Value("${path.image_server_path}")
    private String imageServerPath;
    /**
     * 消息通知策略参数
     */
    @Value("${msg.ftp_ip}")
    private String ftpIp;
    @Value("${msg.ftp_port}")
    private int ftpPort;
    @Value("${msg.ftp_path}")
    private String ftpPath;
    @Value("${msg.ftp_user}")
    private String ftpUser;
    @Value("${msg.ftp_pwd}")
    private String ftpPwd;
    @Value("${msg.back_day}")
    private int backDay;


    @Autowired
    private EffectTotalMapper effectTotalMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public void autoPushTotal() {
        //查询日期
        long date = System.currentTimeMillis() - (long) (Integer.parseInt(pushResultDuration) * 60 * 60 * 1000);
        String qryDate = DateUtil.date2str(new Date(date), DateUtil.DATE_FORMAT_YYYY_MM_DD);
        String taskDate = DateUtil.date2str(new Date(date), DateUtil.DATE_FORMAT_YYYYMMDD);
        //查询push效果统计表参数
        Map<String, Object> param = new HashMap<>(2);
        param.put("isCallBack", Sysconst.PUSH_NOT_BACK);
        param.put("qryDate", qryDate);
        //获取48小时前且统计回调为 1-未回调效果统计 的push策略
        List<Map<String, Object>> list = effectTotalMapper.qryPushTotal(param);
        if (null != list && list.size() > 0) {
            //效果统计入参
            Map<String, Object> totalMap = new HashMap<>();
            //调用http接口入参
            Map<String, Object> paramMap = new HashMap<>(3);
            //header
            Map<String, String> header = new HashMap<>(2);
            //返回结果字符串
            String result = "";
            //任务id
            String taskId;
            for (Map<String, Object> stringMap : list) {
                //策略id + 策略执行日期
                taskId = stringMap.get("tacId").toString() + taskDate;
                //设置HTTP接口post参数
                paramMap.put("appId", PUSH_APP_ID);
                paramMap.put("taskId", taskId);
                paramMap.put("pushChannel", pushChannel);
                header.put("Content-Type", "application/json");
                header.put("charset", "utf-8");
                //调用http接口
                try {
                    result = HttpUtils.doPost(PUSH_IP + pushResultUrl, paramMap, header);
                    log.info("push效果统计返回:{}", result);
                } catch (Exception e) {
                    log.error("调用PUSH结果统计接口失败", e);
                }
                //字符串转json对象
                JSONObject jsonObject = JSONObject.parseObject(result);
                //返回成功
                if (jsonObject.getString("code").equals(Sysconst.SUCCESS)) {
                    //获取数据
                    JSONObject data = jsonObject.getJSONObject("data");
                    //策略ID
                    totalMap.put("tacId", stringMap.get("tacId").toString());
                    //策略执行日期
                    totalMap.put("date", qryDate);
                    //百日联网数--对端用户去重
                    totalMap.put("msgTotal", data.getString("msgTotal"));
                    //在线用户数
                    totalMap.put("onlineNum", data.getString("onlineNum"));
                    JSONObject android = data.getJSONObject("android");
                    //成功下发-android
                    int androidSend = Integer.parseInt(android.getString("sent") != null ? android.getString("sent") : "0");
                    totalMap.put("androidSend", androidSend);
                    //展示数-android
                    int androidDisplayed = Integer.parseInt(android.getString("displayed") != null ? android.getString("displayed") : "0");
                    totalMap.put("androidDisplayed", androidDisplayed);
                    //点击数-android
                    int androidClicked = Integer.parseInt(android.getString("clicked") != null ? android.getString("clicked") : "0");
                    totalMap.put("androidClicked", androidClicked);
                    JSONObject ios = data.getJSONObject("ios");
                    //展示数-ios
                    int iosDisplayed = Integer.parseInt(ios.getString("displayed") != null ? ios.getString("displayed") : "0");
                    totalMap.put("iosDisplayed", iosDisplayed);
                    //点击数-ios
                    int iosClicked = Integer.parseInt(ios.getString("clicked") != null ? ios.getString("clicked") : "0");
                    totalMap.put("iosClicked", iosClicked);
                    //成功下发-ios
                    int iosSend = Integer.parseInt(ios.getString("sent") != null ? ios.getString("sent") : "0");
                    totalMap.put("iosSend", iosSend);
                    //点击总数
                    totalMap.put("totalClicked", androidClicked + iosClicked);
                    //展示总数
                    totalMap.put("totalDisplayed", androidDisplayed + iosDisplayed);
                    //下发总数
                    totalMap.put("totalSend", androidSend + iosSend);
                    log.info("androidSend:{}, " +
                                    "androidDisplayed:{}, " +
                                    "androidClicked:{}, " +
                                    "iosDisplayed:{}, " +
                                    "iosSend:{}, " +
                                    "iosClicked:{}, " +
                                    "totalSend:{}, " +
                                    "totalDisplayed:{}, " +
                                    "totalClicked:{}",
                            androidSend, androidDisplayed, androidClicked, iosDisplayed, iosSend, iosClicked, androidSend + iosSend, androidDisplayed + iosDisplayed, androidClicked + iosClicked);
                    //转化率=点击数/展示数   --百分数
                    DecimalFormat df = new DecimalFormat("0.00");
                    //android转化率
                    if (androidDisplayed != 0) {
                        double androidCrv = Double.parseDouble(df.format((float) androidClicked / androidDisplayed * 100));
                        totalMap.put("androidCrv", androidCrv);
                    }
                    //ios转化率
                    if (iosDisplayed != 0) {
                        double iosCrv = Double.parseDouble(df.format((float) iosClicked / iosDisplayed * 100));
                        totalMap.put("iosCrv", iosCrv);
                    }
                    //总转化率
                    if (androidDisplayed + iosDisplayed != 0) {
                        double totalCrv = Double.parseDouble(df.format((float) (androidClicked + iosClicked) / (androidDisplayed + iosDisplayed) * 100));
                        totalMap.put("totalCrv", totalCrv);
                    }
                    //状态改为 2-已经完成回调
                    totalMap.put("isCallBack", Sysconst.PUSH_BACK);
                    //更新push效果统计表
                    effectTotalMapper.updateCenPush(totalMap);
                } else {
                    log.info("调用获取PUSH效果统计接口失败；{}", jsonObject.getString("msg"));
                }
            }
        } else {
            log.info("没有可回调的push策略效果统计");
        }
    }

    @Override
    public void autoMsgTotal() {
        //获取ftp连接
        FTPClient client = FtpUtil.getClient(ftpIp, ftpPort, ftpUser, ftpPwd, ftpPath);
        //获取ftp目录下所有文件名字
        List<String> fileNames = FtpUtil.getFileNameList(ftpPath, client);
        //list结果集
        List<String> list = new LinkedList<>();
        //从第一个文件开始读取
        if (isHaveFile(fileNames, getFileName(1))) {
            boolean flag = true;
            int i = 1;
            while (flag) {
                List<String> data = FtpUtil.readFile(getFileName(i), client);
                if (data != null && data.size() != 0) {
                    list.addAll(data);
                }
                i++;
                flag = isHaveFile(fileNames, getFileName(i));
            }
        } else {
            log.info("文件不存在：{}", getFileName(1));
        }
        //更新通知页pvuv
        updatePvUv(client);
        //关闭当前ftp
        FtpUtil.closeServer(client);
        //对list结果集分组统计处理
        Map<String, Map<String, Long>> msg = dealResult(list);
        //遍历msg
        Map<String, Object> param = new HashMap<>(3);
        for (String key : msg.keySet()) {
            //清空map
            param.clear();
            //需要更新的任务id  任务id = 策略id + 当日时间  所以是唯一的，可作为key
            param.put("taskId", key);
            //先把已读未读数默认为0,第二次统计不会存在未读数
            param.put("singleRead", 0);
            param.put("singleUnread", 0);
            //遍历key对应的value
            Iterator<Map.Entry<String, Long>> iterator = msg.get(key).entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<String, Long> entry = iterator.next();
                if (entry.getKey().equals("1")) {
                    param.put("singleRead", entry.getValue());
                }
                if (entry.getKey().equals("0")) {
                    param.put("singleUnread", entry.getValue());
                }
            }
            //更新效果统计表
            try {
                effectTotalMapper.updateCenMsg(param);
                log.info("消息通知效果统计更新成功，任务id：{}", key);
            } catch (Exception e) {
                log.info("消息通知效果统计更新失败，任务id：{}", key);
                e.printStackTrace();
            }
        }
        //更新效果统计状态  0-创建；1-效果统计周期中；2-统计完成
        //策略执行日期超过10天状态为2，其余执行过定时任务后状态为1
        effectTotalMapper.updateCenMsgStatus();
    }

    @Override
    public void statusToGoing() {
        // 获取当前时间字符串
        String now = DateUtil.date2str(new Date(), DateUtil.DATE_TIME_FORMAT_YYYY_MM_DD_HH_MI_SS);
        Map<String, Object> map = new HashMap<>(3);
        map.put("now", now);
        map.put("currentStatus", Sysconst.NOT_START);
        map.put("updateStatus", Sysconst.GOING);
        try {
            List<Map<String, Object>> newUserInfo = effectTotalMapper.getNewUserInfo();
            if (!CollectionUtils.isEmpty(newUserInfo)) {
                String redisKey = "new_channel_tacs";
                newUserInfo.forEach(k -> {
                    String tacId = String.valueOf(k.get("tacId"));
                    String tacMode = String.valueOf(k.get("tacMode"));
                    StringBuffer sb = new StringBuffer();
                    if (null != k.get("channelIds")) {
                        sb.append(k.get("channelIds"));
                    }
                    sb.append("_");
                    sb.append(tacId);
                    sb.append("_");
                    sb.append(tacMode);
                    sb.append("_");
                    switch (String.valueOf(k.get("tacMode"))) {
                        case "1":
                            sb.append("1");
                            break;
                        case "3":
                            sb.append("4");
                            break;
                        default:
                            sb.append("2");
                    }
                    log.info("key：{}， value：{}", tacId, sb);
                    Map<String, String> channel = new HashMap<>();
                    channel.put(tacId, String.valueOf(sb));
                    log.info("新增redis记录：{}", redisKey);
                    redisTemplate.opsForHash().putAll(redisKey, channel);
                });
            }
            effectTotalMapper.statusToGoing(map);
            log.info("策略状态已更新");
        } catch (Exception e) {
            log.info("策略状态更新失败");
            e.printStackTrace();
        }
    }

    @Override
    public void statusToEnd() {
        Map<String, Object> map = new HashMap<>(2);
        map.put("currentStatus", Sysconst.GOING);
        map.put("updateStatus", Sysconst.END);
        try {
            effectTotalMapper.statusToEnd(map);
            log.info("策略状态已更新");
        } catch (Exception e) {
            log.info("策略状态更新失败");
            e.printStackTrace();
        }
    }

    /**
     * 拼接文件名
     *
     * @param i
     * @return
     */
    private String getFileName(int i) {
        //获取前一天日期
        Date lastDate = new Date(System.currentTimeMillis() - (1000 * 60 * 60 * 24 * backDay));
        //文件前缀
        String pro = "message_" + DateUtil.date2str(lastDate, DateUtil.DATE_FORMAT_YYYYMMDD);
        //根据参数判断拼接完整文件名
        if (i < 10) {
            return pro.concat("_00").concat(String.valueOf(i)).concat(".txt");
        } else if (i < 100) {
            return pro.concat("_0").concat(String.valueOf(i)).concat(".txt");
        } else {
            return pro.concat("_").concat(String.valueOf(i)).concat(".txt");
        }
    }

    /**
     * 判断指定文件是否存在当前文件list中
     *
     * @param fileNames
     * @param fileName
     * @return
     */
    private boolean isHaveFile(List<String> fileNames, String fileName) {
        return fileNames.stream().filter(f -> f.equals(fileName)).findAny().isPresent();
    }

    /**
     * list结果集处理
     */
    private Map<String, Map<String, Long>> dealResult(List<String> data) {
        //list 转javabean
        List<MessageInfo> list = data.stream().map(e -> new MessageInfo(e.substring(0, e.indexOf("|")),
                e.substring(e.lastIndexOf("|") + 1))).collect(Collectors.toList());
        //按任务id 和 读取状态分组统计
        return list.stream().collect(Collectors.groupingBy(MessageInfo::getTaskId,
                Collectors.groupingBy(MessageInfo::getMsgStatus, Collectors.counting())));
    }

    /**
     * 读取ftp服务器上msgPVUV_日期 文件
     * 文件名字固定，且每天只有一个，里面也只有一条数据   pv|uv
     */
    private void updatePvUv(FTPClient client) {
        //获取前一天日期
        Date lastDate = new Date(System.currentTimeMillis() - (1000 * 60 * 60 * 24));
        //文件前缀
        String fileName = "msgPVUV_" + DateUtil.date2str(lastDate, DateUtil.DATE_FORMAT_YYYYMMDD) + ".txt";
        //读取结果
        List<String> list = FtpUtil.readFile(fileName, client);
        String[] strs = new String[]{"0", "0"};
        if (list != null && list.size() != 0) {
            strs = list.get(0).split("\\|");
        }
        Map<String, Object> map = new HashMap<>(3);
        map.put("noticePv", Integer.valueOf(strs[0]));
        map.put("noticeUv", Integer.valueOf(strs[1]));
        map.put("lastDate", DateUtil.date2str(lastDate, DateUtil.DATE_FORMAT_YYYY_MM_DD));
        try {
            effectTotalMapper.updateCenMsgPVUV(map);
            log.info("策略执行日期通知页pvuv更新成功");
        } catch (Exception e) {
            log.info("策略执行日期通知页pvuv更新失败");
            e.printStackTrace();
        }
    }
}
